<template>
  <view class="" :style="tbStyle">
    <view v-if="!props.hideHeader" class="grid" :style="thStyle" :class="props.thClass">
      <view v-for="(col, index) in columns" :key="index" :style="thStyleCell(col)" :class="[props.thClass, col.thClass]">
        {{ col.title }}
      </view>
    </view>
    <view v-for="(row, index) in props.data" :key="index" class="grid" :style="{ ...getRowStyle(row, index), gridTemplateColumns }" :class="getRowClass(row, index)">
      <view v-for="(col, colIndex) in columns" :key="colIndex" :style="getRowCellStyle(row, col, index)" :class="getRowCellClass(row, col, index)">
        <u-image v-if="row.format === 'image'" :src="col.key && row[col.key]" :width="col.width" :height="col.height" />

        <rich-text v-else-if="col.format === 'html'" :nodes="getRowText(row, col, index)" />

        <text v-else>
          {{ getRowText(row, col, index) }}
        </text>
      </view>
    </view>
    <view v-if="!props.data.length" class="text-sm text-#999 flex-center h-80">
      暂无数据
    </view>
  </view>
</template>

<script setup lang="ts">

import type { CSSProperties } from 'vue'
import type { Align, Column, Columns } from '@/types'
import { dateFormat, datetimeFormat, timeFormat } from '@/utils/datetime'
import UImage from '@/uni_modules/vk-uview-ui/components/u-image/u-image.vue'

const props = withDefaults(defineProps<{
  data: Record<string, any>[]
  columns: Columns
  align?: Align
  minWidth?: number
  tbStyle?: CSSProperties
  thStyle?: CSSProperties
  thClass?: string
  thCellStyle?: CSSProperties
  thCellClass?: string
  rowStyle?: CSSProperties | ((row: Record<string, any>, index: number) => CSSProperties)
  rowClass?: string | ((row: Record<string, any>, index: number) => string)
  hideHeader?: boolean
}>(), {
  minWidth: 100,
  align: 'center',
})

const dict = useDict()

const columns = computed<Columns>(() => {
  const typeTitle = (col: Column) => {
    switch (col.type) {
      case 'operation':
        return '操作'
      case 'index':
        return '序号'
      default:
        return undefined
    }
  }

  const typeFixed = (col: Column) => {
    switch (col.type) {
      case 'operation':
        return 'right'
      case 'selection':
        return 'left'
      default:
        return undefined
    }
  }

  return props.columns.map((it) => ({
    ...it,
    title: it.title || typeTitle(it),
    fixed: it.fixed || typeFixed(it),
  }))
})

const gridTemplateColumns = computed(() => (columns.value
  .map((col) => {
    if (col.width) {
      return Number.isNaN(Number(col.width))
        ? col.width
        : `${col.width}rpx`
    }
    if (col.type === 'selection') {
      return '50rpx'
    }
    const min = col.minWidth || props.minWidth
    const max = col.maxWidth || '1fr'
    return `minmax(${
      Number.isNaN(Number(min)) || !min ? min : `${min}rpx`
    }, ${Number.isNaN(Number(max)) ? max : `${max}rpx`})`
  })
  .join(' ')))

const tbStyle = computed(() => (<CSSProperties>{
  border: '2rpx solid #D6DDF1',
  borderRadius: '8rpx',
  fontSize: '28rpx',
  color: '#333',
  ...props.tbStyle,
  width: 'fit-content',
  minWidth: '100%',
}))

const thStyle = computed(() => (<CSSProperties>{
  color: '#666666',
  fontWeight: 400,
  backgroundColor: props.columns[0].type !== 'index' ? '#EAEEF7' : 'white',
  borderBottom: '2rpx solid #D6DDF1',
  ...props.thStyle,
  gridTemplateColumns: gridTemplateColumns.value,
  width: 'fix-content',
}))

function thStyleCell(col: Column) {
  return {
    display: 'flex',
    justifyContent: props.align,
    alignItems: 'center',
    padding: '10rpx 20rpx',
    minHeight: '80rpx',
    ...props.thCellStyle,
    ...col.thStyle,
  }
}

const getRowText = (row: Record<string, any>, col: Column, index: number) => {
  let text

  if (col.getText) {
    text = col.getText(row, col.key ? row[col.key] : null)
  } else if (col.type === 'index') {
    return index + 1
  } else if (col.key) {
    text = row[col.key]
  }

  if (text && col.format) {
    if (col.format === 'dict') return dict.getName(col.dict || col.key || '', text)
    if (col.format === 'date') return dateFormat(text)
    if (col.format === 'datetime') return datetimeFormat(text)
    if (col.format === 'time') return timeFormat(text)
  }

  if (Array.isArray(text)) {
    return (text as Array<any>).join()
  }

  return text
}

function getRowStyle(row: Record<string, any>, index: number) {
  const css = typeof props.rowStyle === 'function' ? props.rowStyle(row, index) : props.rowStyle
  return {
    minHeight: '80rpx',
    borderTop: index === 0 ? '' : '2rpx solid #D6DDF1',
    boxSizing: 'content-box',
    ...css,
  }
}

function getRowClass(row: Record<string, any>, index: number) {
  return typeof props.rowClass === 'function' ? props.rowClass(row, index) : props.rowClass
}

function getRowCellStyle(row: Record<string, any>, col: Column, index: number) {
  const css = typeof col.style === 'function' ? col.style(row, index) : col.style
  return {
    minHeight: '80rpx',
    wordBreak: 'break-all',
    ...css,
    display: 'flex',
    alignItems: 'center',
    padding: '10rpx 20rpx',
    justifyContent: col.type === 'index' ? 'center' : (col.align || props.align),
  }
}

function getRowCellClass(row: Record<string, any>, col: Column, index: number) {
  return typeof col.class === 'function' ? col.class(row, index) : col.class
}

</script>

<style scoped>

</style>
